package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.common.autoconfigure.redisson.annotations.Lock;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.enums.CouponStatus;
import com.tianji.promotion.domain.enums.ExchangeCodeStatus;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.UserCouponQuery;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.mapper.UserCouponMapper;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.utils.CodeUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-11-23
 */
@Service
@RequiredArgsConstructor
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {
    private final CouponMapper couponMapper;
    private final IExchangeCodeService codeService;

    /**
     * 领取优惠券
     *
     * @param couponId
     */
    @Override
    public void receiveCoupon(Long couponId) {
        //1.查询优惠券是否存在
        Coupon coupon = couponMapper.selectById(couponId);
        if (ObjectUtils.isEmpty(coupon)) {
            throw new IllegalArgumentException("优惠券不存在");
        }
        //2.判断优惠券是否正在发放
        if (coupon.getStatus() != CouponStatus.ISSUING) {
            throw new IllegalArgumentException("优惠券未开始发放");
        }
        //3.判断库存是否足够
        if (coupon.getIssueNum() >= coupon.getTotalNum()) {
            throw new IllegalArgumentException("优惠券已领完");
        }

        //5.开始修改数据库保存优惠券领取信息
        Long userId = UserContext.getUser();
        //代理对象调用方法
        /*IUserCouponService userCouponService = (IUserCouponService) AopContext.currentProxy();
        userCouponService.saveUserCoupon(coupon, user);*/
        ((UserCouponServiceImpl) AopContext.currentProxy()).saveUserCoupon(coupon, userId);
    }

    /**
     * 兑换码兑换优惠券
     *
     * @param code
     */
    @Override
    @Transactional
    public void exchangeCoupon(String code) {
        //1.校验兑换码
        long serialNum = CodeUtil.parseCode(code);
        //2.校验是否兑换过
        Boolean exchanged = codeService.updateExchangeMark(serialNum, true);
        if (exchanged) {
            throw new IllegalArgumentException("兑换码已兑换");
        }
        //失败了事务需要回滚,redis也要手动回滚
        try {
            //3.查询兑换码响应的优惠券
            ExchangeCode exchange = codeService.getById(serialNum);
            if (ObjectUtils.isEmpty(exchange)) {
                throw new IllegalArgumentException("兑换码不存在");
            }
            LocalDateTime now = LocalDateTime.now();
            //4.判断兑换码是否过期
            if (now.isAfter(exchange.getExpiredTime())) {
                throw new IllegalArgumentException("兑换码已过期");
            }
            //5.校验并生成用户券
            Long user = UserContext.getUser();
            Coupon coupon = couponMapper.selectById(exchange.getExchangeTargetId());
            //默认事务传播特性是required，所以方法事务会加入
            saveUserCoupon(coupon, user);
            //6.更新兑换码状态
            codeService.lambdaUpdate()
                    .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
                    .set(ExchangeCode::getUserId, user)
                    .eq(ExchangeCode::getId, serialNum)
                    .update();
        } catch (IllegalArgumentException e) {
            codeService.updateExchangeMark(serialNum, false);
            throw e;
        }

    }

    /**
     * 用于保存用户领取优惠券记录以及修改优惠券信息
     *
     * @param coupon
     * @param user
     */
    @Transactional
    @Override
    @Lock(name = "lock:coupon:#{userId}")
    public void saveUserCoupon(Coupon coupon, Long user) {
        //4.判断该用户是否领取超出限领数量
        //4.1聚合查询用户已经领取数量
        Integer count = this.lambdaQuery()
                .eq(UserCoupon::getCouponId, coupon.getId())
                .eq(UserCoupon::getUserId, user)
                .count();
        if (count == null) {
            count = 0;
        }
        //4.2判断是否到达限领数量
        if (count >= coupon.getUserLimit()) {
            throw new IllegalArgumentException("优惠券已领完");
        }
        //5.1修改优惠券表，乐观锁
        int r = couponMapper.incrIssueNum(coupon.getId());
        if (r == 0) {
            throw new IllegalArgumentException("优惠券已领完");
        }
        //5.2保存用户领取优惠券记录
        UserCoupon userCoupon = new UserCoupon();
        userCoupon.setUserId(user);
        userCoupon.setCouponId(coupon.getId());
        LocalDateTime termBeginTime = coupon.getTermBeginTime();
        LocalDateTime termEndTime = coupon.getTermEndTime();
        if (termBeginTime == null) {
            termBeginTime = LocalDateTime.now();
            termEndTime = termBeginTime.plusDays(coupon.getTermDays());
        }
        userCoupon.setTermBeginTime(termBeginTime);
        userCoupon.setTermEndTime(termEndTime);
        save(userCoupon);
    }

    /**
     * 查询我的优惠券
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<CouponVO> queryMyCouponPage(UserCouponQuery query) {
        //1.查询我的优惠券，收集所有优惠券id
        Long user = UserContext.getUser();
        Page<UserCoupon> page = this.lambdaQuery()
                .eq(UserCoupon::getUserId, user)
                .eq(UserCoupon::getStatus, query.getStatus())
                .page(query.toMpPage());
        List<UserCoupon> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        //1.1收集优惠券id
        List<Long> couponIds = records.stream().map(UserCoupon::getCouponId).collect(Collectors.toList());

        //2.查询优惠券详情
        List<Coupon> coupons = couponMapper.selectBatchIds(couponIds);

        //3.封装vo
        //3.1数据拷贝
        List<CouponVO> couponVOS = BeanUtils.copyList(coupons, CouponVO.class);
        return PageDTO.of(page, couponVOS);
    }
}
